package server;

/**
 * Created by IntelliJ IDEA.
 * User: sam
 * Date: 05/02/12
 * Time: 10:43
 * To change this template use File | Settings | File Templates.
 *
 */

//import com.sun.deploy.Environment;
//import com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException;
import org.apache.commons.dbcp.BasicDataSource;
        import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.servlet.*;
import javax.servlet.http.*;
        import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
        import java.sql.*;
        import java.util.*;

        import org.apache.commons.fileupload.servlet.ServletFileUpload;


//import org.apache.log4j.Logger;
//import org.apache.log4j.PropertyConfigurator;

//import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
//import org.apache.commons.dbcp.BasicDataSource;

/**
 *
 * On first getParameter() call, the HttpServletRequest will internally use getInputStream() to read
 * and parse the request body (it's a byte stream from a network connection) and
 * store it in a map which you can get by getParameterMap(). After this point, you can't read
 * the request body by getInputStream()/getReader() anymore, because it's already been read.
 *
 * http://localhost:8080/sample/sample
 * http://localhost:8080/musicfinder
 *
 * home/sam/Downloads/commons-fileupload-1.2.2/lib
 * home/sam/Downloads/commons-io-2.1
 * home/sam/Downloads/httpcomponents-client-4.1.3/lib
 *
 * export CLASSPATH=/usr/share/java/servlet.jar:/home/sam/Downloads/commons-fileupload-1.2.2/lib/commons-fileupload-1.2.2.jar:/home/sam/Downloads/commons-io-2.1/commons-io-2.1.jar:/home/sam/Downloads/httpcomponents-client-4.1.3/lib/commons-logging-1.1.1.jar:/home/sam/Downloads/httpcomponents-client-4.1.3/lib/httpclient-4.1.3.jar:/home/sam/Downloads/httpcomponents-client-4.1.3/lib/httpcore-4.1.4.jar:/home/sam/Downloads/httpcomponents-client-4.1.3/lib/httpmime-4.1.3.jar:/home/sam/Downloads/javamail-1.4.5/mail.jar
 *
 * sudo cp commons-fileupload-1.2.2.jar ~/tomcat/lib/
 * sudo cp /home/sam/tomcat/lib/commons-io-2.1.jar ~/tomcat/lib/
 *
 * added "/home/sam/tomcat/lib/commons-fileupload-1.2.2.jar" and /home/sam/tomcat/lib/commons-io-2.1.jar
 * to project SDK list for ServletFileUpload import:
 *
 * http://commons.apache.org/fileupload/download_fileupload.cgi
 *
 * NB: There are existing commongs pkgs in tomcat lib (that tomcat itself uses, e.g. pooling?)
 *
 * LOG4J: added "/home/sam/tomcat/lib/log4j-1.2.16.jar" (or /home/sam/Downloads/apache-log4j-1.2.16/log4j-1.2.16.jar) in this IDE setting
 *
 *
 * TOMCAT_OPTS=-Dlog4j.configuration=file:/home/sam/tomcat/webapps/musicfinder/WEB-INF/classes/properties/
 *
 * As already stated, log4j.properties should be in a directory included in the classpath (/home/sam/tomcat/webapps/musicfinder/WEB-INF/classes/)
 *
 *
 *
 * http://www.coderanch.com/t/355272/Servlets/java/HttpURLConnection-Posting-XML-servlet
 *
 *  mysql -u root:
 *
 * set password for sam@localhost = password('sn8pplefl8sh');
 *
 * create database musicfinder;
 *
 * grant CREATE,INSERT,DELETE,UPDATE,SELECT,DROP,DELETE,ALTER on musicfinder.* to sam@localhost;
 *
 * flush privileges;
 *  mysql -h localhost -u sam -p
 *
 *  netstat -tln or telnet localhost 3306s
 *
 *  http://zetcode.com/databases/mysqljavatutorial/
 *
 *  http://www.mysql.com/downloads/connector/j/
 *
 *  cp mysql-connector-java-5.1.18-bin.jar in to tomcat/lib
 *
 *  Access denied for user 'sam'@'localhost' to database 'musicfinder/'
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Access denied for user 'sam'@'localhost' to database 'musicfinder/'


To create a HttpServlet that forwards all incoming requests as is, to another serlvet running on a different domain.
The RequestDispatcher's forward() only operates on the same server

 http://davidhuo.blogspot.com/2006/05/be-careful-with-httpservletrequestgeti.html

 You can't when it doesn't run in the same ServletContext or same/clustered webserver wherein the webapps are configured to share the
 ServletContext (in case of Tomcat, check crossContext option).

You have to send a redirect by HttpServletResponse.sendRedirect().
 If your actual concern is reusing the query parameters on the new URL, just resend them along.

 /usr/share/java/servlet.jar:
 /home/sam/Downloads/commons-fileupload-1.2.2/lib/commons-fileupload-1.2.2.jar:
 /home/sam/Downloads/commons-io-2.1/commons-io-2.1.jar:
 /home/sam/Downloads/httpcomponents-client-4.1.3/lib/commons-logging-1.1.1.jar:
 /home/sam/Downloads/httpcomponents-client-4.1.3/lib/httpclient-4.1.3.jar:
 /home/sam/Downloads/httpcomponents-client-4.1.3/lib/httpcore-4.1.4.jar:
 /home/sam/Downloads/httpcomponents-client-4.1.3/lib/httpmime-4.1.3.jar:
 /home/sam/google_svn/music-alms/src/:
 /home/sam/Downloads/javamail-1.4.5/mail.jar:
 /home/sam/Downloads/javamail-1.4.5/lib/mailapi.jar:
 /home/sam/Downloads/commons-codec-1.4.jar:
 /home/sam/Downloads/mysql-connector-java-5.1.18/mysql-connector-java-5.1.18-bin.jar:
 /home/sam/Downloads/commons-dbutils-1.4/commons-dbutils-1.4.jar:
 /home/sam/Downloads/commons-dbcp-1.4/commons-dbcp-1.4.jar:
 /home/sam/Downloads/commons-pool-1.6/commons-pool-1.6.jar
 *
 */


public class MFServer extends MFHttpServlet {

    //private static org.apache.log4j.Logger LOG = Logger.getLogger(MFServer.class);

    protected Properties props;   // java.util.Properties or jdbc.sql.Properties ?

    private String db_url;
    private String db_user;
    private String db_pwd;
    private String db_driver;

    //private Connection db_con;
    //private Statement db_st;
    //private ResultSet db_rs;
    //private Document doc = null;
    //private int bytesTotal;
    //private ServletOutputStream servletOutputStream;
    //private BufferedOutputStream bufferedOutputStream;

    private BasicDataSource jdbcPooledDataSource;
    private Connection connection;
    private Document req_xml;

    // You can suppress this warning with @SuppressWarnings("serial"), or give the class a serialVersionUID:
    // server/MFServer.java:131: warning: [serial] serializable class server.MFServer has no definition of serialVersionUID
    private static final long serialVersionUID = 123456;

    //private int numUploads = 0; //OUCH!!! Only bit of domain/meta data we store at class level... hack
    //String UPLOAD_LOCATION = "/home/sam/mf_server_dump/music_finder_uploads"+File.separator;
    String UPLOAD_LOCATION = "/home/sam/Music/music-alms-rep"+File.separator;
    //String DOWNLOAD_LOCATION = "/home/sam/Music/music-alms-rep/uploads"+File.separator;

    public MFServer() {
    }

    @Override
    public void init(ServletConfig config) throws ServletException {

        System.out.println("MFServer.init(config)");

        /**
         * Did you override the init(ServletConfig) method without calling
         * super.init(ServletConfig)? The NPE is originating in GenericServlet.getServletContext(),
         * which in turn calls getServletConfig()---the latter is setup upon initialisation by GenericServlet,
         * and if you overrode that method but didn't set the variable yourself, it will still be null.
         * If that doesn't do it, how about posting your entire Servlet class?
         */
        super.init(config);

        Properties props = new Properties();   // http://commons.apache.org/dbutils/apidocs/index.html

        try {


                    //properties/server.properties
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("C:\\Users\\Sam\\google_code_svn\\music-alms\\properties");

            props.load(inputStream);

            db_url = props.getProperty("db_url");
            db_user = props.getProperty("db_user");
            db_pwd = props.getProperty("db_pwd");
            db_driver = props.getProperty("db_driver");

            // retrieved from context in GenreServlet
            //String emailUser = props.getProperty("music-alms_admin");
            //String emailPass = props.getProperty("music-alms_pass");
            //String emailHost = props.getProperty("music-alms_host");

            ServletContext servletContext = getServletContext();

            // global data, non req specific info
            //servletContext.setAttribute("upload_location", UPLOAD_LOCATION);
            servletContext.setAttribute("properties", props);

            //servletContext.setAttribute("music-alms.sql.login",props.getProperty("music-alms.sql.login"));

        } catch (IOException e) {
            e.printStackTrace();
        }

        jdbcPooledDataSource = new BasicDataSource();

        jdbcPooledDataSource.setDriverClassName(db_driver);
        jdbcPooledDataSource.setUrl(db_url);
        jdbcPooledDataSource.setUsername(db_user);
        jdbcPooledDataSource.setPassword(db_pwd);
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        System.out.println("MFServer.doPost()"+"\n");


        try {
            connection = jdbcPooledDataSource.getConnection();

            if (connection != null) {

                ServletContext servletContext = getServletContext();
                servletContext.setAttribute("db_connection", connection);

                doPostWork(request,response);
            }
        }
        catch (SQLException sqlException) {
            sqlException.printStackTrace();
        }
        finally {
            if (connection != null) {
                try {
                    connection.close();
                }
                catch (SQLException e) {
                }
                finally {

                }
            }
          }
   }

    private void doPostWork(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        System.out.println("MFServer.doPostWork()"+"\n");
        System.out.println(request.toString());

        InputStreamReader inputStreamReader = new InputStreamReader(request.getInputStream());

        String contentType = request.getContentType();

        // Utility method that determines whether the request contains multipart content
        boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);

        System.out.println("contentType " + contentType + "\n");
        System.out.println("isMultipartContent " + isMultipartContent + "\n");

        //if((contentType != null) && (contentType.indexOf("multipart/form-data") >= 0)) {
        //MFProgressListener multipartProgListener = new MFProgressListener();
        //HttpSession session = request.getSession();
	    //session.setAttribute("multipartProgListener", multipartProgListener);

        /**
         * Since forward() method of RequestDispatcher is handled on the server ,
         * therefore the request and its associated session are available to the
         * forwarded resource and you can pass data between them using request.setAttribute().
         * forward() separates the responsibilities for handling the requests among several components.
         * This method generally sends a request and response object to resources (servlets or JSP's)
         * of the same ServletContext. You can also decide to forward to one page in one condition
         * and a different page in another. RequestDispatcher  transfers control immediately
         * whenever the forward() method is called to the resource referenced by the RequestDispatcher.
         */
        String FORWARD_SERVLET;
        RequestDispatcher requestDispatcher = null;

        request.setAttribute("upload_location", UPLOAD_LOCATION);

        if(isMultipartContent) {

            if(userHasValidSession(request, response)) {
                FORWARD_SERVLET = "/UploadServlet";
                requestDispatcher = request.getRequestDispatcher(FORWARD_SERVLET);
                requestDispatcher.forward(request, response);
            }

        } else {

            String requestType = "";
            NodeList nList = null;

            try {
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
                req_xml = dBuilder.parse(new InputSource(inputStreamReader));

                //NodeList nList = req_xml.getElementsByTagName("MFRequest");
                //System.out.println("nList.item(0) "+nList.item(0).getNodeName());

                /**
                 * <MFRequest>
                 *     <Request type='Repositories' data=''/>
                 * </MFRequest>
                 */

                nList = req_xml.getElementsByTagName("Request");
                requestType = nList.item(0).getAttributes().getNamedItem("type").getNodeValue();

                //Node nNode = nList.item(0);
                System.out.println("nList.item(0).getNodeName() "+nList.item(0).getNodeName()); // <MFRequest>
                System.out.println("requestType "+requestType);

                nList = req_xml.getChildNodes();

                //Element eElement = (Element) nNode;
                //requestType = eElement.getAttribute("type");
                //System.out.println("eElement getNodeValue "+nNode.getNodeValue());
                //System.out.println("eElement getAttribute "+eElement.getAttribute("type"));
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (SAXException e) {
                e.printStackTrace();
            }

            // introduced in Servlet 3.0, not 2.3. so use request.getSession().getServletContext()
            // ServletContext thisContext = request.getServletContext("http://samwootton.com:8080/mfservice/music");
            // ServletContext thisContext = getServletContext("http://samwootton.com:8080/mfservice/music");

            /**
             * So use servletContext.setAttribute() to store things that need to be global in scope,
             * and shared between different requests (and therefore must be threadsafe),
             * and request.setAttribute() to store things relating only to the current request
             * (usually no need to worry about thread safety since a request is usually served by a single thread).
             */

            // Can use both? Some data stored in request.setAttribute for fwd
            // Other data stored in Servlet context
            //ServletContext servletContext = request.getSession().getServletContext();
            //ServletContext servletContext = getServletContext();

            FORWARD_SERVLET = getFwdServlet(requestType, request, response);

            System.out.println("FORWARD_SERVLET "+FORWARD_SERVLET);

            if(FORWARD_SERVLET != "") {
                requestDispatcher = request.getRequestDispatcher(FORWARD_SERVLET);

                // request specific data
                //request.setAttribute("db_connection", connection);
                request.setAttribute("request_xml", nList);
                //request.setAttribute("upload_location", UPLOAD_LOCATION);
                requestDispatcher.forward(request, response);

            } else {
                System.out.println("Unknown request type: "+requestType);
                writeErrorResponse(request, response, "Unknown request type");
            }
        }
    }

    private String getFwdServlet(String requestType, HttpServletRequest request, HttpServletResponse response) {

        String fwdServlet = "";

        if(requestType.equals("Login")) {
            // don't have to have valid session pre login
            fwdServlet = "/LoginServlet";
        }else if(requestType.equals("Repositories")) {
            // don't have to have valid session to get repositories,
            // as you have to select repo to login to
            fwdServlet = "/RepositoryServlet";
        }
        // requests that require session checking
        else if(userHasValidSession(request, response)) {

            if(requestType.equals("SaveSettings")){
                fwdServlet = "/SettingsServlet";
            }
            else if(requestType.equals("GetCatagories")) {
                fwdServlet = "/GenreServlet";
            }
            else if(requestType.equals("Search")) {
                fwdServlet = "/SearchServlet";
            }
            else if(requestType.equals("Download")) {
                fwdServlet = "/DownloadServlet";
            }
            else if(requestType.equals("AddGenre")) {
                fwdServlet = "/GenreServlet";
            }
        }

        return fwdServlet;
    }

    private boolean userHasValidSession(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("checkUserSession()");

        HttpSession session = request.getSession(false);    // want to log user out if session-time out has occurred

        if(session != null) {
        } else {

            writeErrorResponse(request, response, "MF_SESSION_EXPIRED");
            return false;
        }

        String tmpHeader = request.getHeader("Set-Cookie");     // JSESSIONID=E250E5826791E2B6DD8448784F8A457D; Path=/musicfinder

        Cookie[] cookies = request.getCookies();

        System.out.println("cookies " + cookies);     // cookies [Ljavax.servlet.http.Cookie;@2b891172

        for(int i = 0; i < cookies.length; i++) {

            String cookieName = cookies[i].getName();
            String cookieValue = cookies[i].getValue();

            System.out.println("cookieName "+cookieName);
            System.out.println("cookieValue "+cookieValue);

            /**
             *  cookies [Ljavax.servlet.http.Cookie;@74184b3b
                cookieName JSESSIONID
                cookieValue B5F9DB26DB30EE0EF768BEED03F947CA
                doSearchRequest() session username sam
             */

            if(cookieName.equals("JSESSIONID")) {
                // Dont actually need to do anything.  It sets cookie implicitly and reads it as too!!!!! wooooop wooop!!!
            }
        }

        String username = (String) session.getAttribute("username");

        System.out.println("checkUserSession() session username "+username);

        return true;
    }
}

